import ustruct

class modbus_toolkit:

    def modbus_01_readCoils(self, modbus_engine, modbus_params, retry_times):
        # print("modbus_01_readCoils params: ", modbus_params["device_addr"], modbus_params["start_addr"], modbus_params["bit_position"])
        read_data = bytearray(1)

        modbus_return = modbus_engine.readCoils(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            4,
            read_data,
            1000,
        )  # 其中的4，就是bit的数量，而不是byte的数量
        # 设备地址为2，从0寄存器开始，读2个作为2个字节，读到readBuf里，超时1秒
        # read_data=ubinascii.unhexlify(read_data)
        # print(
        #     "modbus_01_readCoils result: ", modbus_params["start_addr"], read_data, str(read_data)
        # )
        if modbus_return[0] != 0:

            print(
                "readCoils error:",
                modbus_return,
                modbus_params["device_addr"],
                modbus_params["start_addr"],
                modbus_params["bit_position"],
                read_data,
            )

            if retry_times > 0:
                print(
                    " modbus_01_readCoils *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_01_readCoils(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["bit_position"],
                    retry_times - 1,
                )
            else:
                return None
        strBin = ""
        for b in read_data:
            strBin = str(bin(b))
            strBin = strBin[2 : len(strBin)]

            while len(strBin) < 4:
                strBin = "0" + strBin
        result_value = int(strBin[3 - modbus_params["bit_position"]])
        print("readCoils result:", modbus_params["start_addr"], result_value)
        return result_value

    def modbus_03_readHoldingRegisters_float(
        self,
        modbus_engine,
        modbus_params,
        retry_times,
    ):
        read_data = bytearray(modbus_params["read_length"] * 2)

        modbus_return = modbus_engine.readHoldingRegisters(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            modbus_params["read_length"],
            read_data,
            1000,
        )

        result_value = None

        if modbus_return[0] != 0:

            print(
                "readHoldingRegisters error:",
                modbus_return,
                modbus_params["device_addr"],
                modbus_params["start_addr"],
                read_data,
            )

            if retry_times > 0:
                print(
                    " modbus_03_readHoldingRegisters_float *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_03_readHoldingRegisters_float(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["read_length"],
                    modbus_params["result_rate"],
                    retry_times - 1,
                )
            else:
                return result_value
        # 设备地址为2，从0寄存器开始，读2个作为2个字节，读到readBuf里，超时1秒

        fillHex = [
            "%02X" % i for i in read_data
        ]  # printf 输出的格式 2位十六进制,不足是前面0补充
        fillHex_concated = "".join(fillHex)

        decimal_ori = int(fillHex_concated, 16)
        print(
            "modbus_03_readHoldingRegisters_float",
            modbus_params["start_addr"],
            fillHex_concated,
            decimal_ori,
        )
        decimal_result_array = ustruct.unpack(">f", ustruct.pack("<I", decimal_ori))
        result_value = round(
            decimal_result_array[0] * modbus_params["result_rate"],
            modbus_params["float_count"],
        )

        print(
            "readInputRegisters result:",
            modbus_params["start_addr"],
            result_value,
        )

        return result_value

    def modbus_03_readHoldingRegisters_unsigned32(
        self,
        modbus_engine,
        modbus_params,
        retry_times,
    ):
        # print('---start modbus_read_wenshidu---')
        read_data = bytearray(modbus_params["read_length"] * 2)

        modbus_return = modbus_engine.readHoldingRegisters(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            modbus_params["read_length"],
            read_data,
            1000,
        )
        # 设备地址为2，从0寄存器开始，读2个作为2个字节，读到readBuf里，超时1秒
        """
        ● 返回值：
        返回一个tuple类型的2元组，元组中的条目格式为： 
        ○ status: 请求状态，0表示成功，其他表示失败，具体含义参考 STATUS
        ○ respond_count: 读取到数据的字节数
        """
        result_value = None
        if modbus_return[0] != 0:
            if retry_times > 0:
                print(
                    " modbus_03_readHoldingRegisters_unsigned32 *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_03_readHoldingRegisters_unsigned32(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["read_length"],
                    modbus_params["result_rate"],
                    retry_times - 1,
                )
            else:
                return result_value
        if read_data[0] != None:
            # haasIoTools.func_ledFlash(ledSTA, 'ledSTA','fast', 3)
            result_value = (
                256 * 256 * read_data[0]
                + 256 * 256 * 256 * read_data[1]
                + 256 * read_data[3]
                + read_data[2]
            )

            result_value = result_value * modbus_params["result_rate"]
            return result_value
        else:
            return result_value
        # print('modbus_03_readHoldingRegisters_unsigned raw result_value: ', result_value)

    def modbus_03_readHoldingRegisters_unsigned(
        self,
        modbus_engine,
        modbus_params,
        retry_times,
    ):
        read_data = bytearray(modbus_params["read_length"] * 2)

        modbus_return = modbus_engine.readHoldingRegisters(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            modbus_params["read_length"],
            read_data,
            1000,
        )
        # 设备地址为2，从0寄存器开始，读2个作为2个字节，读到readBuf里，超时1秒

        result_value = None
        if modbus_return[0] != 0:
            if retry_times > 0:
                print(
                    " modbus_03_readHoldingRegisters_unsigned *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_03_readHoldingRegisters_unsigned(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["read_length"],
                    modbus_params["result_rate"],
                    retry_times - 1,
                )
            else:
                return result_value

        i = 0
        if read_data[0] != None:
            # haasIoTools.func_ledFlash(ledSTA, 'ledSTA','fast', 3)
            result_value = 0

            while i < modbus_params["read_length"] * 2:  # 1个字节包括2位
                result_value += read_data[i] * (256**i)
                # print("-", read_data[i])
                i = i + 1
            result_value = result_value * modbus_params["result_rate"]
        # print('modbus_03_readHoldingRegisters_unsigned raw result_value: ', result_value)

        return result_value

    def modbus_04_readInputRegisters_signed(
        self,
        modbus_engine,
        modbus_params,
        retry_times,
    ):
        print("## modbus_04_readInputRegisters_signed")
        read_data = bytearray(modbus_params["read_length"] * 2)

        modbus_return = modbus_engine.readInputRegisters(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            modbus_params["read_length"],
            read_data,
            1000,
        )
        print("## readInputRegisters ", modbus_return)

        result_value = None

        if modbus_return[0] != 0:
            print(
                "modbus_04_readInputRegisters_signed error:",
                modbus_return,
                modbus_params["device_addr"],
                modbus_params["start_addr"],
                read_data,
            )
            if retry_times > 0:
                print(
                    " modbus_04_readInputRegisters_signed *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_04_readInputRegisters_signed(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["read_length"],
                    modbus_params["result_rate"],
                    retry_times - 1,
                )
            else:
                return result_value

        i = 0
        if read_data[0] != None:
            result_value = 0
        else:
            return None

        while i < modbus_params["read_length"] * 2:  # 1个字节包括2位
            result_value += read_data[i] * (256**i)
            print("-", read_data[i])
            i = i + 1

        if result_value < 32767:
            return result_value * modbus_params["result_rate"]
        else:
            return (result_value - 65536) * modbus_params["result_rate"]

    def modbus_04_readInputRegisters_float(
        self,
        modbus_engine,
        modbus_params,
        retry_times,
    ):
        read_data = bytearray(modbus_params["read_length"] * 2)

        modbus_return = modbus_engine.readInputRegisters(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            modbus_params["read_length"],
            read_data,
            1000,
        )
        # 设备地址为2，从0寄存器开始，读2个作为2个字节，读到readBuf里，超时1秒

        result_value = None
        if (
            modbus_return[0] != 0
            or len(read_data) < 1
            or (len(read_data) and read_data[0] == None)
        ):
            if retry_times > 0:
                print(
                    " modbus_04_readInputRegisters_float *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_04_readInputRegisters_float(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["read_length"],
                    modbus_params["float_count"],
                    retry_times - 1,
                )
            else:
                return result_value

        if read_data[0] != None:
            result_value = 0

        fillHex = ["%02X" % i for i in read_data]
        # printf 输出的格式 2位十六进制,不足是前面0补充
        fillHex_concated = "".join(fillHex)

        decimal_ori = int(fillHex_concated, 16)
        decimal_resultArray = ustruct.unpack(">f", ustruct.pack("<I", decimal_ori))
        print("~~~HEX raw", fillHex_concated, decimal_resultArray)
        result_value = round(decimal_resultArray[0], modbus_params["float_count"])

        return result_value

    def modbus_05_writeSingleCoils(self, modbus_engine, modbus_params, retry_times):
        value_to_set_hex = 0xFF00
        if modbus_params["value_to_set"] == 0:
            value_to_set_hex = 0x0000

        modbus_return = modbus_engine.writeCoil(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            value_to_set_hex,
            1000,
        )

        if modbus_return[0] != 0:

            print(
                "modbus_05_writeSingleCoils error:",
                modbus_return,
                modbus_params["device_addr"],
                modbus_params["start_addr"],
            )

            if retry_times > 0:
                print(
                    " modbus_05_writeSingleCoils *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_05_writeSingleCoils(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["value_to_set"],
                    retry_times - 1,
                )
            else:
                return None
        else:
            return True

    def modbus_10_writeMultipleHoldingRegisters_unsigned32(
        self,
        modbus_engine,
        modbus_params,
        retry_times,
    ):

        last_value = modbus_params["tar_long"] % (256 * 256)
        head_value = int((modbus_params["tar_long"] - last_value) / (256 * 256))

        last_value_tail = last_value % 256
        last_value_head = int((last_value - last_value_tail) / 256)

        head_value_tail = head_value % 256
        head_value_head = int((head_value - head_value_tail) / 256)

        hex_arry = [
            head_value_tail,
            head_value_head,
            last_value_tail,
            last_value_head,
        ]
        modbus_return = modbus_engine.writeMultipleHoldingRegisters(
            modbus_params["device_addr"],
            modbus_params["start_addr"],
            int(len(hex_arry) / 2),
            bytearray(hex_arry),
            1000,
        )
        #
        if modbus_return[0] != 0:
            if retry_times > 0:
                print(
                    " modbus_10_writeMultipleHoldingRegisters_unsigned32 *** try again *** ",
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                )
                return self.modbus_10_writeMultipleHoldingRegisters_unsigned32(
                    modbus_engine,
                    modbus_params["device_addr"],
                    modbus_params["start_addr"],
                    modbus_params["tar_long"],
                    retry_times - 1,
                )
            else:
                return None
        else:
            return True
